﻿using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Domain.CodeGenerators
{
    [Generator]
    public class MermaidDomainCodeGenerators : ISourceGenerator
    {

        private static readonly Regex ClassRegex = new(@"class (?<className>\w+)\s*{\s*<<(?<classType>.*?)>>(?<members>[^{]+)}", RegexOptions.Compiled);
        private static readonly Regex ClassFieldRegex = new(@"\+\s*(?<filedType>\w+)\s+(?<filedName>\w+)", RegexOptions.Compiled);
        private static readonly Regex FunctionRegex = new(@"\+\s+(?<filedName>\w+)\s*([(](?<p>.*)?[)])\s*(?<returnType>\w*)");


        public void Execute(GeneratorExecutionContext context)
        {
            if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace))
            {
                var files = context.AdditionalFiles;
                foreach (var file in files)
                {
                    var fileFullName = file.Path;
                    if (!fileFullName.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }
                    var domainName = Path.GetFileNameWithoutExtension(fileFullName);
                    var mermaidString = file.GetText()?.ToString();
                    if (string.IsNullOrEmpty(mermaidString))
                    {
                        continue;
                    }

                    var matches = ClassRegex.Matches(mermaidString);
                    List<string> strongTypeIds = new();
                    foreach (Match match in matches)
                    {
                        var groups = match.Groups;
                        var className = groups["className"].Value;
                        var classType = groups["classType"].Value;
                        var members = groups["members"].Value;

                        var classFields = ClassFieldRegex.Matches(members);

                        if (classType.Equals("DomainEvent", StringComparison.OrdinalIgnoreCase))
                        {

                            var entityName = classFields[0].Groups["filedType"].Value;
                            GenerateDomainEvent(context, rootNamespace, domainName, className, entityName);
                        }
                        else
                        {
                            string idType = string.Empty;
                            StringBuilder memberCode = new();
                            foreach (Match classField in classFields)
                            {
                                var fieldGroups = classField.Groups;
                                var fieldType = fieldGroups["filedType"].Value;
                                var fieldName = fieldGroups["filedName"].Value;


                                if (fieldName.Equals("id", StringComparison.OrdinalIgnoreCase))
                                {
                                    idType = fieldType;
                                    strongTypeIds.Add(fieldType);
                                    continue;
                                }
                                memberCode.AppendLine();
                                memberCode.Append("        ");
                                memberCode.Append($@"public {fieldType} {fieldName} {{ get; protected set; }}");

                            }

                            var functions = FunctionRegex.Matches(members);
                            foreach (Match function in functions)
                            {
                                var functionGroups = function.Groups;
                                var functionName = functionGroups["filedName"].Value;
                                var functionParams = functionGroups["p"].Value;
                                var functionReturnType = functionGroups["returnType"]?.Value;

                                if (functionReturnType != null || functionReturnType == "triggers")
                                {
                                    functionReturnType = "void";
                                }

                                memberCode.AppendLine();
                                memberCode.Append("        ");
                                memberCode.Append($@"public partial {functionReturnType} {functionName}({functionParams});");
                            }

                            GenerateEntity(context, rootNamespace, domainName, className, idType, memberCode.ToString(), classType.Equals("AggregateRoot", StringComparison.OrdinalIgnoreCase));
                        }

                    }


                    foreach (var strongTypeId in strongTypeIds)
                    {
                        string source = $@"// <auto-generated/>
using NetCorePal.Extensions.Domain;
using System.ComponentModel;
namespace {rootNamespace}.{domainName}
{{
    /// <summary>
    /// 订单Id
    /// </summary>
    [TypeConverter(typeof(EntityIdTypeConverter<{strongTypeId}, long>))]
    public record {strongTypeId}(long Id) : IInt64StronglyTypedId
    {{
        public static implicit operator long({strongTypeId} id) => id.Id;
        public static implicit operator {strongTypeId}(long id) => new {strongTypeId}(id);

        public override string ToString()
        {{
            return Id.ToString();
        }}
    }}
}}
";
                        context.AddSource($"StrongTypeId_{domainName}_{strongTypeId}.g.cs", source);

                    }
                }

            }


        }

        public void Initialize(GeneratorInitializationContext context)
        {
            // Method intentionally left empty.
        }


        void GenerateDomainEvent(GeneratorExecutionContext context, string rootNamespace, string domainName, string className, string entityName)
        {
            string source = $@"// <auto-generated/>
using NetCorePal.Extensions.Domain;
using {rootNamespace}.{domainName};
namespace {rootNamespace}.DomainEvents
{{
    public class {className} : IDomainEvent
    {{
        public {className}({entityName} {ToLowerCamelCase(entityName)})
        {{
            {entityName} = {ToLowerCamelCase(entityName)};
        }}

        public {entityName} {entityName} {{ get; }}
    }}
}}
";
            context.AddSource($"DomainEvents_{className}.g.cs", source);
        }

        void GenerateEntity(GeneratorExecutionContext context, string rootNamespace, string domainName, string className, string idType, string memberCode, bool isAggregateRoot)
        {
            string source = $@"// <auto-generated/>
using NetCorePal.Extensions.Domain;
namespace {rootNamespace}.{domainName}
{{
    public partial class {className} : Entity<{idType}>{(isAggregateRoot ? ", IAggregateRoot" : "")}
    {{
        /// <summary>
        /// 受保护的默认构造函数，用以作为EF Core的反射入口
        /// </summary>
        protected {className}() {{ }}
{memberCode}
    }}
}}
";
            context.AddSource($"{domainName}_{className}.g.cs", source);
        }




        string ToLowerCamelCase(string str)
        {
            if (string.IsNullOrEmpty(str)) return str;

            if (str.Length <= 1)
            {
                return str.ToLowerInvariant();
            }

            return char.ToLowerInvariant(str[0]) + str.Substring(1);
        }
    }
}
